package com.it.servlet;/**
 * @ClassName DispatcherServlet
 * @Description: TODO
 * @Author gejianhao
 * @Date 2019/7/30
 * @Version V1.0
 **/

import com.it.controller.IndexController;
import com.it.util.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName DispatcherServlet
 * @Description: TODO
 * @Author gejianhao
 * @Date 2019/7/30
 * @Version V1.0
 **/
public class DispatcherServlet extends HttpServlet {


    private List<String> list = new ArrayList<String>();

    private Map<String, Object> beans = new HashMap<String, Object>();

    private Map<String, Object> handlerMap = new HashMap<String, Object>();

    /**
     * @param config
     * @throws ServletException
     */
    public void init(ServletConfig config) throws ServletException {
        doScanPackages("com.it");
        doInstance();
        doAutowired();
        doUrlMapping();
    }

    //扫描特殊注解的类
    public void doScanPackages(String basePackages) {
        //类加载器,将路径变成计算机可以识别的路径
        URL url = this.getClass().getClassLoader().getResource("/" + basePackages.replaceAll("\\.", "/"));
        String fileStr = url.getFile();
        //获取文件地址
        File file = new File(fileStr);
        String[] filesStr = file.list();
        //File filesStr[]=file.listFiles();
        for (String path : filesStr) {
            File files = new File(fileStr + path);
            if (files.isDirectory()) {
                //遍历扫描类
                doScanPackages(basePackages + "." + path);
            } else {
                list.add(basePackages + "." + files.getName());
            }
        }
    }

    //实例化对象
    public void doInstance() {
        for (String className : list) {
            String cn = className.replaceAll(".class", "");
            try {
                Class<?> clazz = Class.forName(cn);
                //是否使用注解
                if (clazz.isAnnotationPresent(QdtecController.class)) {
                    //实例化对象
                    Object obj = clazz.newInstance();
                    //拿到注解的类
                    QdtecRequestMapping erm = clazz.getAnnotation(QdtecRequestMapping.class);
                    String key = erm.value();//james
                    //ioc放入容器中
                    beans.put(key, obj);
                } else if (clazz.isAnnotationPresent(QdtecService.class)) {
                    //实例化对象
                    Object obj = clazz.newInstance();
                    //拿到注解的类
                    QdtecService erm = clazz.getAnnotation(QdtecService.class);
                    String key = erm.value();//james
                    //ioc放入容器中
                    beans.put(key, obj);
                } else {
                    continue;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void doAutowired() {
        for (Map.Entry<String, Object> map : beans.entrySet()) {
            Object obj = map.getValue();//实例化的类
            Class clazz = obj.getClass();
            if (clazz.isAnnotationPresent(QdtecController.class)) {
                Field[] fields = clazz.getDeclaredFields();//遍历的变量
                for (Field f : fields) {
                    if (f.isAnnotationPresent(QdtecAutowired.class)) {
                        QdtecAutowired ea = f.getAnnotation(QdtecAutowired.class);
                        String key = ea.value();
                        Object ins = beans.get(key);
                        f.setAccessible(true);
                        try {
                            f.set(obj, ins);
                        } catch (Exception e) {

                        }

                    } else {
                        continue;
                    }
                }
            }
        }
    }

    public void doUrlMapping() {
        for (Map.Entry<String, Object> map : beans.entrySet()) {
            Object instance = map.getValue();
            Class<?> clazz = instance.getClass();
            if (clazz.isAnnotationPresent(QdtecController.class)) {
                QdtecRequestMapping erm = clazz.getAnnotation(QdtecRequestMapping.class);
                String classPath = erm.value();
                Method methods[] = clazz.getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(QdtecRequestMapping.class)) {
                        QdtecRequestMapping erm1 = method.getAnnotation(QdtecRequestMapping.class);
                        String methodPath = erm1.value();
                        handlerMap.put(classPath + methodPath, method);
                    }
                }
            }
        }
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String uri = request.getRequestURI();
        String context = request.getContextPath();
        String path = uri.replace(context, "");
        Method method = (Method) handlerMap.get(path);
        IndexController index = (IndexController) beans.get("/" + path.split("/")[1]);
        Object args[] = hand(request, response, method);
        try {
            method.invoke(index, args);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Object[] hand(HttpServletRequest request, HttpServletResponse response, Method method) {
        Class<?>[] paramClazzs = method.getParameterTypes();
        Object args[] = new Object[paramClazzs.length];
        int args_i = 0;
        int index = 0;
        for (Class<?> paramClazz : paramClazzs) {

            if (ServletRequest.class.isAssignableFrom(paramClazz)) {
                args[args_i++] = request;
            }
            if (ServletResponse.class.isAssignableFrom(paramClazz)) {
                args[args_i++] = response;
            }
            Annotation[] paramAns = method.getParameterAnnotations()[index];
            if (paramAns.length > 0) {
                for (Annotation paramAn : paramAns) {
                    if (QdtecRequestParam.class.isAssignableFrom(paramAn.getClass())) {
                        QdtecRequestParam rp = (QdtecRequestParam) paramAn;
                        args[args_i++] = request.getParameter(rp.value());
                    }
                }
            }
            index++;

        }
        return args;
    }


}
